package hamaster.jpbc.gui;

import static hamaster.jpbc.crypt.aes.Rijndael.AES_BLOCK_SIZE;
import static javax.swing.BoxLayout.Y_AXIS;
import hamaster.jpbc.Main;
import hamaster.jpbc.http.DecryptionHandler;
import hamaster.jpbc.http.IBCConnection;
import hamaster.jpbc.mgr.ServerRequestManager;
import hamaster.jpbc.pbc.PBC;
import hamaster.jpbc.pbc.PBCDriver;
import hamaster.jpbc.pbs.PBS;
import hamaster.jpbc.pbs.PersonalParameter;
import hamaster.jpbc.pbs.Signature;
import hamaster.jpbc.util.Hash;
import hamaster.jpbc.util.SymmetricKeyGenerator;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Arrays;
import java.util.Properties;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextArea;
import javax.swing.JTextField;

/**
 * 下载ID文件用到的面板
 * @author <a href="mailto:wangyeee@gmail.com">Wang Ye</a>
 */
public class DownIDPanel extends JPanel implements ActionListener {
	private static final long serialVersionUID = -5902169136219501992L;

	private JDialog container;
	private JTextField emailInput;
	private JPasswordField passwordInput;
	private JButton download;
	private JButton cancel;
	private JTextArea requestMessage;

	public DownIDPanel(JDialog container) {
		setLayout(new BoxLayout(this, Y_AXIS));
		this.container = container;
		emailInput = new JTextField(10);
		passwordInput = new JPasswordField(10);
		download = new JButton(Messages.getString("DownIDPanel.0")); //$NON-NLS-1$
		cancel = new JButton(Messages.getString("DownIDPanel.1")); //$NON-NLS-1$
		download.addActionListener(this);
		cancel.addActionListener(this);
		requestMessage = new JTextArea(8, 10);
		requestMessage.setVisible(false);
		requestMessage.setEditable(false);

		JPanel ePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		JPanel pPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		JPanel dPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		ePanel.add(new JLabel(Messages.getString("DownIDPanel.2"))); //$NON-NLS-1$
		ePanel.add(emailInput);
		pPanel.add(new JLabel(Messages.getString("DownIDPanel.3"))); //$NON-NLS-1$
		pPanel.add(passwordInput);
		dPanel.add(cancel);
		dPanel.add(download);
		add(ePanel);
		add(pPanel);
		add(dPanel);
		add(requestMessage);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == this.download) {
			String email = emailInput.getText();
			if (email == null || email.length() < 1) {
				JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.4"), Messages.getString("DownIDPanel.5"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				emailInput.requestFocus();
				return;
			}
			if (!email.matches("\\w+(\\.\\w+)*@\\w+(\\.\\w+)+")) { //$NON-NLS-1$
				JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.7"), Messages.getString("DownIDPanel.8"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				emailInput.selectAll();
				emailInput.requestFocus();
				return;
			}
			char[] tp = passwordInput.getPassword();
			if (tp == null || tp.length < 5) {
				JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.9"), Messages.getString("DownIDPanel.10"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				passwordInput.requestFocus();
				return;
			}
			String password = new String(tp);
			Arrays.fill(tp, (char) 0);
			// email ok, password ok
			ServerRequestManager srm = new ServerRequestManager();
			final Properties p = srm.requestID(email, password);
			requestMessage.setVisible(true);
			container.pack();
			Thread t = new Thread() {
				@Override
				public void run() {
					byte[] sessionKeyRaw = SymmetricKeyGenerator.nextKey(48);
					byte[] iv = new byte[AES_BLOCK_SIZE];
					byte[] key = new byte[32];
					System.arraycopy(sessionKeyRaw, 0, iv, 0, AES_BLOCK_SIZE);
					System.arraycopy(sessionKeyRaw, AES_BLOCK_SIZE, key, 0, 32);
					IBCConnection conn = new IBCConnection();
					conn.setPublicParameter(Main.publicParameter());
					conn.setRequest(p);
					requestMessage.setText(null);
					try {
						URL u = new URL(Main.getIdUrl());
						conn.setServerID(Hash.sha1(u.getHost()));
						conn.setSessionKey(sessionKeyRaw);
						conn.setUrl(u);
					} catch (MalformedURLException ex) {
						JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.11"), Messages.getString("DownIDPanel.12"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					}
					requestMessage.append(Messages.getString("DownIDPanel.13")); //$NON-NLS-1$
					DecryptionHandler dh = new DecryptionHandler(container, iv, key);
					conn.setResponseHandler(dh);
					try {
						requestMessage.append(Messages.getString("DownIDPanel.14")); //$NON-NLS-1$
						conn.connect();
						requestMessage.append(Messages.getString("DownIDPanel.15")); //$NON-NLS-1$
					} catch (SocketTimeoutException e) {
						JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.16"), Messages.getString("DownIDPanel.17"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					} catch (IOException e) {
						JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.18"), Messages.getString("DownIDPanel.19"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					}
					requestMessage.append(Messages.getString("DownIDPanel.20")); //$NON-NLS-1$
					for (;;) {
						if (dh.hasHandled()) {
							DownIDPanel.this.handleResponse(dh.getResponse());
							requestMessage.append(Messages.getString("DownIDPanel.21")); //$NON-NLS-1$
							break;
						}
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {}
					}
				}
			};
			t.start();
			return;
		}
		if (e.getSource() == this.cancel) {
			container.setVisible(false);
		}
	}

	public void handleResponse(Properties response) {
		String ec = response.getProperty("errorcode"); //$NON-NLS-1$
		int errorCode;
		try {
			errorCode = Integer.parseInt(ec);
		} catch (Exception e) {
			JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.23"), Messages.getString("DownIDPanel.24"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		}
		switch (errorCode) {
		case 4:
			JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.25"), Messages.getString("DownIDPanel.26"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		case 3:
			JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.27"), Messages.getString("DownIDPanel.28"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		case 2:
			JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.29"), Messages.getString("DownIDPanel.30"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		case 1:
			JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.31"), Messages.getString("DownIDPanel.32"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
			return;
		case 0:
			String keyStr = response.getProperty("idkey"); //$NON-NLS-1$
			String signStr = response.getProperty("idsign"); //$NON-NLS-1$
			String serverParamStr = response.getProperty("serverparams"); //$NON-NLS-1$
			String hashMethod = response.getProperty("idsignmethod"); //$NON-NLS-1$
			ByteArrayInputStream kin = new ByteArrayInputStream(PBCDriver.unhex(keyStr));
			ByteArrayInputStream sin = new ByteArrayInputStream(PBCDriver.unhex(signStr));
			ByteArrayInputStream pin = new ByteArrayInputStream(PBCDriver.unhex(serverParamStr));
			Properties keyp = new Properties();
			Properties signp = new Properties();
			Properties paramp = new Properties();
			try {
				keyp.load(kin);
				signp.load(sin);
				paramp.load(pin);
			} catch (IOException e) {}
			Signature signature = new Signature(signp, null);
			PersonalParameter parameter = new PersonalParameter(paramp, null);
			byte[] hashRaw = signature.getHash();
			String hashStr = PBCDriver.hex(hashRaw, hashRaw.length - signature.getHashLength(), signature.getHashLength());
			try {
				Method hash = Hash.class.getMethod(hashMethod, String.class);
				byte[] hash0 = (byte[]) hash.invoke(null, Hash.propertiesToHexString(keyp));
				if (!hashStr.equalsIgnoreCase(PBCDriver.hex(hash0))) {
					JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.37"), Messages.getString("DownIDPanel.38"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					return;
				}
			} catch (SecurityException e) {
				e.printStackTrace();
				JOptionPane.showMessageDialog(container, e.getLocalizedMessage(), Messages.getString("DownIDPanel.39"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$
				return;
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
				JOptionPane.showMessageDialog(container, e.getLocalizedMessage(), Messages.getString("DownIDPanel.40"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$
				return;
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			boolean verify = PBS.instance(PBC.instance()).verify(signature, parameter);
			if (!verify) {
				JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.41"), Messages.getString("DownIDPanel.42"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				return;
			}
			ByteArrayInputStream bin = new ByteArrayInputStream(PBCDriver.unhex(keyStr));
			Properties keyProps = new Properties();
			try {
				keyProps.load(bin);
			} catch (IOException e) {
				JOptionPane.showMessageDialog(container, Messages.getString("DownIDPanel.43"), Messages.getString("DownIDPanel.44"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				return;
			}
			keyProps.setProperty(Signature.SERVER_SIGNATURE_KEY, signStr);
			keyProps.setProperty(Signature.SERVER_PARAMETER_KEY, serverParamStr);
			JFileChooser save = new JFileChooser();
			save.setSelectedFile(new File(emailInput.getText() + ".id")); //$NON-NLS-1$
			save.setDialogTitle(Messages.getString("DownIDPanel.46")); //$NON-NLS-1$
			save.setVisible(true);
			int returnVal = save.showDialog(container, Messages.getString("DownIDPanel.47")); //$NON-NLS-1$
		    if (returnVal == JFileChooser.APPROVE_OPTION) {
				File idFile = save.getSelectedFile();
				if (idFile.exists()) {
					String format = Messages.getString("DownIDPanel.48"); //$NON-NLS-1$
					int overwrite = JOptionPane.showConfirmDialog(container, String.format(format, idFile.getName()), Messages.getString("DownIDPanel.49"), JOptionPane.YES_NO_OPTION); //$NON-NLS-1$
					if (overwrite == JOptionPane.NO_OPTION)
						return;
				} else {
					try {
						idFile.createNewFile();
					} catch (IOException e) {
						String format = Messages.getString("DownIDPanel.50"); //$NON-NLS-1$
						JOptionPane.showMessageDialog(container, String.format(format, idFile.getName()), Messages.getString("DownIDPanel.51"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$
						return;
					}
				}
				try {
					OutputStream out = new FileOutputStream(idFile);
					keyProps.store(out, null);
					out.close();
					String format = Messages.getString("DownIDPanel.52"); //$NON-NLS-1$
					JOptionPane.showMessageDialog(container, String.format(format, idFile.getName()), Messages.getString("DownIDPanel.53"), JOptionPane.INFORMATION_MESSAGE); //$NON-NLS-1$
				} catch (IOException e) {
					String format = Messages.getString("DownIDPanel.54"); //$NON-NLS-1$
					JOptionPane.showMessageDialog(container, String.format(format, idFile.getName()), Messages.getString("DownIDPanel.55"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$
				}
		    }
		}
	}
}
